////
// @file tableTest.cc
// @brief
// 测试存储管理
//
// @author niexw
// @email niexiaowen@uestc.edu.cn
//
#include "../catch.hpp"
#include <db/table.h>
#include <db/block.h>
#include <db/buffer.h>
using namespace db;

namespace {
void dump(Table &table)
{
    // 打印所有记录，检查是否正确
    int rcount = 0;
    int bcount = 0;
    for (Table::BlockIterator bi = table.beginBlock(); bi != table.endBlock();
         ++bi, ++bcount) {
        for (unsigned short i = 0; i < bi->getSlots(); ++i, ++rcount) {
            Slot *slot = bi->getSlotsPointer() + i;
            Record record;
            record.attach(
                bi->buffer_ + be16toh(slot->offset), be16toh(slot->length));

            unsigned char *pkey;
            unsigned int len;
            long long key;
            record.refByIndex(&pkey, &len, 0);
            memcpy(&key, pkey, len);
            key = be64toh(key);
            printf(
                "key=%lld, offset=%d, rcount=%d, blkid=%d\n",
                key,
                be16toh(slot->offset),
                rcount,
                bcount);
        }
    }
    printf("total records=%zd\n", table.recordCount());
}
bool check(Table &table)
{
    int rcount = 0;
    int bcount = 0;
    long long okey = 0;
    for (Table::BlockIterator bi = table.beginBlock(); bi != table.endBlock();
         ++bi, ++bcount) {
        for (DataBlock::RecordIterator ri = bi->beginRecord();
             ri != bi->endRecord();
             ++ri, ++rcount) {
            unsigned char *pkey;
            unsigned int len;
            long long key;
            ri->refByIndex(&pkey, &len, 0);
            memcpy(&key, pkey, len);
            key = be64toh(key);
            if (okey >= key) {
                dump(table);
                printf("check error %d, okey=%lld key=%lld\n", rcount, okey, key);
                return true;
            }
            okey = key;
        }
    }
    return false;
}
} // namespace

TEST_CASE("db/table.h")
{
    SECTION("less")
    {
        Buffer::BlockMap::key_compare compare;
        const char *table = "hello";
        std::pair<const char *, unsigned int> key1(table, 1);
        const char *table2 = "hello";
        std::pair<const char *, unsigned int> key2(table2, 1);
        bool ret = !compare(key1, key2);
        REQUIRE(ret);
        ret = !compare(key2, key1);
        REQUIRE(ret);
    }

    SECTION("open")
    {
        // NOTE: schemaTest.cc中创建
        Table table;
        int ret = table.open("table");
        REQUIRE(ret == S_OK);
        REQUIRE(table.name_ == "table");
        REQUIRE(table.maxid_ == 1);
        REQUIRE(table.idle_ == 0);
        REQUIRE(table.first_ == 1);
        REQUIRE(table.info_->key == 0);
        REQUIRE(table.info_->count == 3);
    }

    SECTION("bi")
    {
        Table table;
        int ret = table.open("table");
        REQUIRE(ret == S_OK);

        Table::BlockIterator bi = table.beginBlock();
        REQUIRE(bi.block.table_ == &table);
        REQUIRE(bi.block.buffer_);

        unsigned int blockid = bi->getSelf();
        REQUIRE(blockid == 1);
        REQUIRE(blockid == bi.bufDesc->blockid);
        REQUIRE(bi.bufDesc->ref == 1);

        Table::BlockIterator bi1 = bi;
        REQUIRE(bi.bufDesc->ref == 2);
        bi.bufDesc->relRef();

        ++bi;
        bool bret = bi == table.endBlock();
        REQUIRE(bret);
    }

    SECTION("locate")
    {
        Table table;
        table.open("table");

        long long id = htobe64(5);
        int blkid = table.locate(&id, sizeof(id));
        REQUIRE(blkid == 1);
        id = htobe64(1);
        blkid = table.locate(&id, sizeof(id));
        REQUIRE(blkid == 1);
        id = htobe64(32);
        blkid = table.locate(&id, sizeof(id));
        REQUIRE(blkid == 1);
    }

    // 插满一个block
    SECTION("insert")
    {
        Table table;
        table.open("table");
        DataType *type = table.info_->fields[table.info_->key].type;

        // 检查表记录
        long long records = table.recordCount();
        REQUIRE(records == 0);
        Table::BlockIterator bi = table.beginBlock();
        REQUIRE(bi->getSlots() == 4); // 已插入4条记录，但表上没记录
        bi.release();
        // 修正表记录
        BufDesc *bd = kBuffer.borrow("table", 0);
        REQUIRE(bd);
        SuperBlock super;
        super.attach(bd->buffer);
        super.setRecords(4);
        super.setDataCounts(1);
        REQUIRE(!check(table));

        // table = id(BIGINT)+phone(CHAR[20])+name(VARCHAR)
        // 准备添加
        std::vector<struct iovec> iov(3);
        long long nid;
        char phone[20];
        char addr[128];

        // 先填充
        int i, ret;
        for (i = 0; i < 91; ++i) {
            // 构造一个记录
            nid = rand();
            // printf("key=%lld\n", nid);
            type->htobe(&nid);
            iov[0].iov_base = &nid;
            iov[0].iov_len = 8;
            iov[1].iov_base = phone;
            iov[1].iov_len = 20;
            iov[2].iov_base = (void *) addr;
            iov[2].iov_len = 128;

            // locate位置
            unsigned int blkid =
                table.locate(iov[0].iov_base, (unsigned int) iov[0].iov_len);
            // 插入记录
            ret = table.insert(blkid, iov);
            REQUIRE(table.insert(blkid, iov) == EEXIST);
            if (ret == EEXIST) { printf("id=%lld exist\n", be64toh(nid)); }
            if (ret == EFAULT) break;
        }
        // 这里测试表明再插入到91条记录后出现分裂
        // REQUIRE(i + 4 == table.recordCount());
        REQUIRE(!check(table));
    }

    SECTION("split")
    {
        Table table;
        table.open("table");
        DataType *type = table.info_->fields[table.info_->key].type;

        Table::BlockIterator bi = table.beginBlock();

        unsigned short slot_count = bi->getSlots();
        size_t space = 162; // 前面的记录大小

        // 测试split，考虑插入位置在一半之前
        unsigned short index = (slot_count / 2 - 10); // 95/2-10
        std::pair<unsigned short, bool> ret = bi->splitPosition(space, index);
        REQUIRE(ret.first == 47); // 95/2=47，同时将新表项算在内
        REQUIRE(ret.second);

        // 在后半部分
        index = (slot_count / 2 + 10); // 95/2+10
        ret = bi->splitPosition(space, index);
        REQUIRE(ret.first == 48); // 95/2=47，同时将新表项算在内
        REQUIRE(!ret.second);

        // 在中间的位置上
        index = slot_count / 2; // 47
        ret = bi->splitPosition(space, index);
        REQUIRE(ret.first == 47); // 95/2=47，同时将新表项算在内
        REQUIRE(ret.second);

        // 在中间后一个位置上
        index = slot_count / 2 + 1; // 48
        ret = bi->splitPosition(space, index);
        REQUIRE(ret.first == 48); // 95/2=47，同时将新表项算在内
        REQUIRE(!ret.second);

        // 考虑space大小，超过一半
        space = BLOCK_SIZE / 2;
        index = (slot_count / 2 - 10); // 95/2-10
        ret = bi->splitPosition(space, index);
        REQUIRE(ret.first == index); // 未将新插入记录考虑在内
        REQUIRE(!ret.second);

        // space>1/2，位置在后部
        index = (slot_count / 2 + 10); // 95/2+10
        ret = bi->splitPosition(space, index);
        REQUIRE(ret.first == 48); // 95/2=47，同时将新表项算在内
        REQUIRE(!ret.second);

        // 测试说明，这个分裂策略可能使得前一半小于1/2
    }

    SECTION("allocate")
    {
        Table table;
        table.open("table");

        REQUIRE(table.dataCount() == 1);
        REQUIRE(table.idleCount() == 0);

        REQUIRE(table.maxid_ == 1);
        unsigned int blkid = table.allocate();
        REQUIRE(table.maxid_ == 2);
        REQUIRE(table.dataCount() == 2);

        Table::BlockIterator bi = table.beginBlock();
        REQUIRE(bi.bufDesc->blockid == 1);
        ++bi;
        REQUIRE(bi == table.endBlock()); // 新分配block未插入数据链
        REQUIRE(table.idle_ == 0);       // 也未放在空闲链上
        bi.release();

        // 回收该block
        table.deallocate(blkid);
        REQUIRE(table.idleCount() == 1);
        REQUIRE(table.dataCount() == 1);
        REQUIRE(table.idle_ == blkid);

        // 再从idle上分配
        blkid = table.allocate();
        REQUIRE(table.idleCount() == 0);
        REQUIRE(table.maxid_ == 2);
        REQUIRE(table.idle_ == 0);
        table.deallocate(blkid);
        REQUIRE(table.idleCount() == 1);
        REQUIRE(table.dataCount() == 1);
    }

    SECTION("insert2")
    {
        Table table;
        table.open("table");
        DataType *type = table.info_->fields[table.info_->key].type;

        // 准备添加
        std::vector<struct iovec> iov(3);
        long long nid;
        char phone[20];
        char addr[128];

        // 构造一个记录
        nid = rand();
        type->htobe(&nid);
        iov[0].iov_base = &nid;
        iov[0].iov_len = 8;
        iov[1].iov_base = phone;
        iov[1].iov_len = 20;
        iov[2].iov_base = (void *) addr;
        iov[2].iov_len = 128;

        int ret = table.insert(1, iov);
        REQUIRE(ret == S_OK);

        Table::BlockIterator bi = table.beginBlock();
        REQUIRE(bi.bufDesc->blockid == 1);
        REQUIRE(bi->getSelf() == 1);
        REQUIRE(bi->getNext() == 2);
        unsigned short count1 = bi->getSlots();
        ++bi;
        REQUIRE(bi->getSelf() == 2);
        REQUIRE(bi->getNext() == 0);
        unsigned short count2 = bi->getSlots();
        REQUIRE(count1 + count2 == 96);
        REQUIRE(count1 + count2 == table.recordCount());
        REQUIRE(!check(table));

        // dump(table);
        // bi = table.beginblock();
        // bi->shrink();
        // dump(table);
        // bi->reorder(type, 0);
        // dump(table);
        // REQUIRE(!check(table));
    }

    // 再插入10000条记录
    SECTION("insert3")
    {
        Table table;
        table.open("table");
        DataType *type = table.info_->fields[table.info_->key].type;

        // 准备添加
        std::vector<struct iovec> iov(3);
        long long nid;
        char phone[20];
        char addr[128];

        iov[0].iov_base = &nid;
        iov[0].iov_len = 8;
        iov[1].iov_base = phone;
        iov[1].iov_len = 20;
        iov[2].iov_base = (void *) addr;
        iov[2].iov_len = 128;

        int count = 96;
        int count2 = 0;
        for (int i = 0; i < 10000; ++i) {
            nid = rand();
            type->htobe(&nid);
            // locate位置
            unsigned int blkid =
                table.locate(iov[0].iov_base, (unsigned int) iov[0].iov_len);
            // 插入记录
            int ret = table.insert(blkid, iov);
            if (ret == S_OK) ++count;
        }

        for (Table::BlockIterator bi = table.beginBlock();
             bi != table.endBlock();
             ++bi)
            count2 += bi->getSlots();
        REQUIRE(count == count2);
        REQUIRE(count == table.recordCount());
        REQUIRE(table.idleCount() == 0);

        REQUIRE(!check(table));
    }
    SECTION("update")
    {
        Table table;
        table.open("table");
        DataType *type = table.info_->fields[table.info_->key].type;

        // 准备测试数据
        std::vector<struct iovec> iov(3);
        long long target_id = 0xABCD1234; // 测试键值
        char phone[20] = "old_phone";
        char addr[128] = "old_address";

        // 构造记录（使用与insert相同的字节序处理）
        long long nid = target_id;
        type->htobe(&nid); // 使用type的htobe方法统一字节序转换
        iov[0].iov_base = &nid;
        iov[0].iov_len = 8;
        iov[1].iov_base = phone;
        iov[1].iov_len = 20;
        iov[2].iov_base = addr;
        iov[2].iov_len = 128;

        // 插入记录 - 使用与insert相同的locate+insert流程
        unsigned int blkid =
            table.locate(iov[0].iov_base, (unsigned int) iov[0].iov_len);
        int ret = table.insert(blkid, iov);
        REQUIRE(ret == S_OK);

        // --- 测试1：更新非键字段 ---
        char new_phone[20] = "new_phone_123456";
        char new_addr[128] = "new_address_with_update";

        // 构造更新数据（保持与insert相同的iov结构）
        std::vector<struct iovec> new_iov(3);
        new_iov[0].iov_base = &nid; // 保持原键值不变
        new_iov[0].iov_len = 8;
        new_iov[1].iov_base = new_phone;
        new_iov[1].iov_len = 20;
        new_iov[2].iov_base = new_addr;
        new_iov[2].iov_len = 128;

        // 执行更新 - 使用与insert相同的locate流程
        blkid = table.locate(
            new_iov[0].iov_base, (unsigned int) new_iov[0].iov_len);
        ret = table.update(blkid, new_iov);
        REQUIRE(ret == S_OK);

        // 验证更新结果 - 使用与insert测试相同的迭代器检查方式
        int found_count = 0;
        for (Table::BlockIterator bi = table.beginBlock();
             bi != table.endBlock();
             ++bi) {
            for (DataBlock::RecordIterator ri = bi->beginRecord();
                 ri != bi->endRecord();
                 ++ri) {
                long long current_id;
                unsigned char *pkey;
                unsigned int len;
                ri->refByIndex(&pkey, &len, 0);

                memcpy(&current_id, pkey, len);
                type->betoh(&current_id); // 使用type的betoh方法统一字节序转换

                if (current_id == target_id) {
                    found_count++;
                    // 检查更新后的字段
                    unsigned char *pphone, *paddr;
                    ri->refByIndex(&pphone, &len, 1);
                    REQUIRE(memcmp(pphone, new_phone, 20) == 0);

                    ri->refByIndex(&paddr, &len, 2);
                    REQUIRE(memcmp(paddr, new_addr, 128) == 0);
                }
            }
        }
        REQUIRE(found_count == 1);

        // --- 测试2：更新不存在的记录 ---
        long long non_exist_id = rand();
        type->htobe(&non_exist_id);
        new_iov[0].iov_base = &non_exist_id;
        blkid = table.locate(
            new_iov[0].iov_base, (unsigned int) new_iov[0].iov_len);
        ret = table.update(blkid, new_iov);
        REQUIRE(ret == ENOENT);
        REQUIRE(!check(table));
    }
    // 测试删除基本功能
    SECTION("remove")
    {
        Table table;
        table.open("table");
        DataType *type = table.info_->fields[table.info_->key].type;

        // 检查初始记录数
        long long initial_records = table.recordCount();
        REQUIRE(initial_records > 0); // 表中应有之前插入测试的数据

        // 准备删除数据结构
        std::vector<struct iovec> iov(3);
        long long nid;
        char phone[20];
        char addr[128];

        iov[0].iov_base = &nid;
        iov[0].iov_len = 8;
        iov[1].iov_base = phone;
        iov[1].iov_len = 20;
        iov[2].iov_base = (void *) addr;
        iov[2].iov_len = 128;

        // 随机删除50条记录
        int removed = 0;
        for (int i = 0; i < 50; ++i) {
            nid = rand();      // 使用与插入测试相同的随机生成方式
            type->htobe(&nid); // 转换字节序

            // 定位记录位置
            unsigned int blkid =
                table.locate(iov[0].iov_base, (unsigned int) iov[0].iov_len);

            // 尝试删除
            int ret = table.remove(
                blkid, iov[0].iov_base, (unsigned int) iov[0].iov_len);
            if (ret == S_OK) { removed++; }
        }

        // 验证删除结果
        REQUIRE(!check(table));
        // 验证记录数变化
        REQUIRE(initial_records - removed == table.recordCount());
    }
    // 测试删除特定记录
    SECTION("remove2")
    {
        // 初始化表对象
        Table table;
        table.open("table"); // 打开数据表

        // 获取主键字段的数据类型
        DataType *type = table.info_->fields[table.info_->key].type;

        // 获取表的第一个数据块
        Table::BlockIterator bi = table.beginBlock();
        REQUIRE(bi != table.endBlock()); // 确保迭代器有效

        // 检查当前块是否有记录，若无则移动到下一个块
        if (bi->getSlots() == 0) {
            ++bi;
            REQUIRE(bi != table.endBlock()); // 确保新块有效
        }

        // 准备删除操作的数据结构
        std::vector<struct iovec> iov(1); // 只需键字段
        long long nid = 0;                // 用于存储键值

        // 生成随机键值
        nid = rand();
        // 转换字节序
        type->htobe(&nid);

        // 设置iovec结构
        iov[0].iov_base = &nid; // 键值指针
        iov[0].iov_len = 8;     // BIGINT固定8字节

        // 记录删除前的总记录数
        auto initial_count = table.recordCount();

        // 定位记录所在块
        unsigned int blkid =
            table.locate(iov[0].iov_base, (unsigned int) iov[0].iov_len);

        // 执行删除操作
        int ret =
            table.remove(blkid, iov[0].iov_base, (unsigned int) iov[0].iov_len);

        // 验证操作结果
        if (ret == S_OK) {
            REQUIRE(initial_count - 1 == table.recordCount()); // 删除成功
        } else {
            REQUIRE(initial_count == table.recordCount()); // 删除失败
        }

        // 检查表的完整性
        REQUIRE(!check(table));
    }
    // 测试批量删除
    SECTION("remove3")
    {
        Table table;
        table.open("table");
        DataType *type = table.info_->fields[table.info_->key].type;

        // 准备删除数据结构
        std::vector<struct iovec> iov(3); // id+phone+addr
        long long nid;
        char phone[20];
        char addr[128];

        iov[0].iov_base = &nid;  // 键字段
        iov[0].iov_len = 8;      // BIGINT固定8字节
        iov[1].iov_base = phone; // 电话字段
        iov[1].iov_len = 20;
        iov[2].iov_base = addr; // 地址字段
        iov[2].iov_len = 128;

        // 记录初始总数
        auto initial_count = table.recordCount();
        decltype(initial_count) removed = 0; // 匹配recordCount类型

        // 尝试删除5000条记录
        for (int i = 0; i < 5000; ++i) {
            nid = rand();      // 使用相同随机生成方式
            type->htobe(&nid); // 保持一致的字节序处理

            // 定位记录位置
            unsigned int blkid =
                table.locate(iov[0].iov_base, (unsigned int) iov[0].iov_len);

            // 执行删除
            int ret = table.remove(
                blkid, iov[0].iov_base, (unsigned int) iov[0].iov_len);
            if (ret == S_OK) { removed++; }
        }

        // 验证总数变化（两种方式交叉验证）
        decltype(initial_count) current_count = 0;
        for (Table::BlockIterator bi = table.beginBlock();
             bi != table.endBlock();
             ++bi) {
            current_count += bi->getSlots(); // 实际统计记录数
        }

        // 验证记录数一致性
        REQUIRE(initial_count - removed == current_count);
        REQUIRE(initial_count - removed == table.recordCount());

        // 验证表的完整性
        REQUIRE(!check(table));
    }
    SECTION("btree insert")
    {
        // 初始化表对象
        Table table;
        table.open("table"); // 打开数据表

        // 获取主键字段的数据类型
        DataType *type = table.info_->fields[table.info_->key].type;

        std::vector<struct iovec> iov(3); // id+phone+addr
        long long nid;
        char phone[20];
        char addr[128];

        iov[0].iov_base = &nid;  // 键字段
        iov[0].iov_len = 8;      // BIGINT固定8字节
        iov[1].iov_base = phone; // 电话字段
        iov[1].iov_len = 20;
        iov[2].iov_base = addr; // 地址字段
        iov[2].iov_len = 128;

        for (int i = 0; i <= 500; ++i) {
            nid = rand();      // 使用相同随机生成方式
            type->htobe(&nid); // 保持一致的字节序处理

            table.insert(iov);
            REQUIRE(table.locateBTree(iov[0].iov_base, (unsigned int) iov[0].iov_len) != 0);
            REQUIRE(table.insert(iov) == EEXIST); // 重复插入应失败
        }
    }
    SECTION("btree remove")
    {
        Table table;
        table.open("table");
        DataType *type = table.info_->fields[table.info_->key].type;

        // 准备测试数据（简化结构）
        std::vector<struct iovec> iov(3);
        long long key;
        char phone[20] = "test_phone";
        char addr[128] = "test_addr";
        iov[0].iov_base = &key;
        iov[0].iov_len = sizeof(key);
        iov[1].iov_base = phone; // 电话字段
        iov[1].iov_len = 20;
        iov[2].iov_base = addr; // 地址字段
        iov[2].iov_len = 128;

        // 测试1: 基本删除功能
        SECTION("basic")
        {
            key = rand();
            type->htobe(&key);

            // 然后插入新记录
            int insert_result = table.insert(iov);
            REQUIRE(insert_result == S_OK);

            // 验证删除
            REQUIRE(table.remove(&key, sizeof(key)) == S_OK);
            REQUIRE(!table.locateBTree(&key, sizeof(key)));
        }

        // 测试2: 批量操作
        SECTION("bulk")
        {
            const int TEST_COUNT = 100; // 进一步减少测试数量
            int success_count = 0;
            for (int i=0; i<TEST_COUNT; ++i) {
                key = 3000 + i;
                type->htobe(&key);
                success_count += (table.insert(iov) == S_OK);
            }
            for (int i = 0; i < TEST_COUNT; i++) {
                key = 3000 + i; // 使用新的键值范围
                type->htobe(&key);
                REQUIRE(table.remove(&key, sizeof(key)) == S_OK);
                for (int j=0;j<=i; ++j) {
                    long long check_key = 3000 + j;
                    type->htobe(&check_key);
                    REQUIRE(table.locateBTree(&check_key, sizeof(check_key)) == 0); // 确保记录已删除
                }
                success_count--;
            }
        }

        // 测试3: 边界值
        SECTION("boundary")
        {
            key = 0;
            type->htobe(&key);
            unsigned int blkid = table.locate(&key, sizeof(key));

            // 先清理
            table.remove(blkid, &key, sizeof(key));

            // 测试插入删除
            if (table.insert(blkid, iov) == S_OK) {
                REQUIRE(table.remove(blkid, &key, sizeof(key)) == S_OK);
            }
        }
    }
}
